package com.example.foo.order.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.ruoyi.common.utils.StringUtils;
import com.example.support.alipay.Alipay;
import com.example.support.wechat.boot.AliPayProperties;
import com.example.support.wechat.boot.WechatProperties;
import com.example.foo.alipay.domain.params.AlipayBean;
import com.example.foo.alipay.domain.params.AlipayNotifyParam;
import com.example.foo.ftt.service.FttProfitFacadeService;
import com.example.foo.order.OrderException;
import com.example.foo.order.model.*;
import com.example.foo.product.model.Item;
import com.example.foo.product.service.ItemService;
import com.example.foo.user.UserHelper;
import com.example.foo.user.model.User;
import com.example.foo.user.service.UserService;
import com.example.foo.weixin.PicaException;
import com.example.foo.weixin.domain.params.TransactionDoResp;
import com.example.foo.weixin.utils.Utility;
import com.example.foo.weixin.wxpay.config.IWxPayConfig;
import com.example.foo.weixin.wxpay.sdk.WXPay;
import com.example.foo.weixin.wxpay.sdk.WXPayUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.util.*;

import static com.example.foo.weixin.wxpay.sdk.WXPayConstants.SUCCESS;

@Service
public class TransactionServiceImpl implements TransactionService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private WechatProperties wechatProperties;
    @Autowired
    private AliPayProperties aliPayProperties;
    @Autowired
    private Alipay alipay;
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private ItemService itemService;
    @Autowired
    private FttProfitFacadeService fttProfitFacadeService;

    @Override
    public TransactionDoResp transactionDo(Order order, HttpServletRequest request) {
        String appId = order.getAppId();
        WechatProperties.Wechat wechat = this.wechatProperties.getMappings().get(appId);

        TransactionDoResp resp = new TransactionDoResp();
        logger.info("transactionDo start, param={}", order);

        User user = userService.findUserById(order.getUid());
        Long itemId = order.getItemId();
        Item item = itemService.findItemById(itemId);

        String openid = "";
        if(order.getPayType().equals(Order.PayType.WX_JSAPI.text)){
            // 微信关注openId
            openid = UserHelper.obtainWechatUserOpenid(appId, user);
        }

        //订单总金额
        final BigDecimal totalPrice = item.getUnitPrice();
        //订单减免金额 随机抵扣1~3元 保留一位小数
        BigDecimal deductionAmount =new BigDecimal((double) (Math.random()*3) + 1);
        Double d=deductionAmount.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        if(d>3) d=3.0;
        deductionAmount =new BigDecimal(String.valueOf(d));

        String out_trade_no = Utility.generateUUID();
        //获取请求ip地址
        String ip = getIpAddr(request);

        //创建订单
        orderService.createOrder(out_trade_no,order.getCardSn(),order.getRealName(),order.getUid(),item,deductionAmount,order.getUserPhone(),order.getProvince(),order.getCity(),order.getDistrict(),order.getAddressDetail(),order.getGoodsDetail(),order.getGoodsType(),totalPrice,order.getHandlingMethod(),order.getPayType(),order.getLxChannelId(),appId);
        // 发起微信支付
        try {
            WXPay wxpay = new WXPay(new IWxPayConfig(wechat));

            /*
             * sp 1. 拼装请求参数
             */
            Map<String, String> data = new HashMap<>();
            data.put("body", "订单基本信息");
            data.put("out_trade_no", out_trade_no);
            data.put("total_fee", String.valueOf((totalPrice.subtract(deductionAmount)).multiply(new BigDecimal(100)).intValue()));
            data.put("spbill_create_ip", ip);
            if(order.getPayType().equals(Order.PayType.WX_APP.text)){
                data.put("trade_type", "APP");
            }else if(order.getPayType().equals(Order.PayType.WX_JSAPI.text)){
                data.put("openid", openid);
                data.put("trade_type", "JSAPI");
            }
            data.put("notify_url", wechat.getTrade().getNotifyUrl()); // 订单回调接口
            data.put("time_expire", Utility.fmtYmdHms(Utility.getCurrentTimeStamp() + 600));

            /*
             * sp 2. 调用微信支付下单接口
             */
            logger.info("发起微信支付下单接口, request={}", data);
            Map<String, String> response = wxpay.unifiedOrder(data);
            logger.info("微信支付下单, 返回值 response={}", response);
            String returnCode = response.get("return_code");
            if (!SUCCESS.equals(returnCode)) {
                return null;
            }
            String resultCode = response.get("result_code");
            if (!SUCCESS.equals(resultCode)) {
                return null;
            }
            String prepay_id = response.get("prepay_id");
            if (prepay_id == null) {
                return null;
            }

            /*
             * sp 3. 进行自己的业务逻辑，保存下单结果
             */
//            order.setOrderNo(orderGid);
//            order.setPayTime(LocalDateTime.now());
//            order.setPayType(response.get("trade_type"));
//            orderService.createOrder(order);

            /*
             * sp 4. 处理下单结果内容，生成前端调起微信支付的sign
             */
            final String nonceStr = Utility.generateUUID();
            final String timeStamp = String.valueOf(System.currentTimeMillis());
            final String packages = "prepay_id=" + prepay_id;
            Map<String, String> wxPayMap = new HashMap<>();
            String sign = "";
            if(order.getPayType().equals(Order.PayType.WX_APP.text)){
                wxPayMap.put("appid", wechat.getMp().getAppId());
                wxPayMap.put("partnerid", wechat.getPay().getMchId());
                wxPayMap.put("prepayid", prepay_id);
                wxPayMap.put("package", "Sign=WXPay");
                wxPayMap.put("noncestr", nonceStr);
                wxPayMap.put("timestamp", timeStamp);
                sign = WXPayUtil.generateSignature(wxPayMap, wechat.getPay().getKey());
            }else if(order.getPayType().equals(Order.PayType.WX_JSAPI.text)){
                wxPayMap.put("appId", wechat.getMp().getAppId());
                wxPayMap.put("timeStamp", timeStamp);
                wxPayMap.put("nonceStr", nonceStr);
                wxPayMap.put("package", packages);
                wxPayMap.put("signType", "MD5");
                sign = WXPayUtil.generateSignature(wxPayMap, wechat.getPay().getKey());
            }

            resp.setAppId(wechat.getMp().getAppId());
            resp.setTimeStamp(timeStamp);
            resp.setNonceStr(nonceStr);
            resp.setSignType("MD5");
            resp.setPrepayId(prepay_id);
            resp.setSign(sign);
            resp.setPartnerId(wechat.getPay().getMchId());
        } catch (Exception e) {
            logger.error("transactionDo error", e);
            throw new PicaException(e.getMessage(), e);
        }
        return resp;
    }

    @Override
    public String aliPay(Order order) throws AlipayApiException {
        String appId = order.getAppId();
        String out_trade_no = Utility.generateUUID();
        Long itemId = order.getItemId();
        Item item = itemService.findItemById(itemId);
        //订单总金额
        final String totalPrice = item.getUnitPrice().toString();
        BigDecimal totalAmount = new BigDecimal(totalPrice);

        orderService.createOrder(out_trade_no,order.getCardSn(),order.getRealName(),order.getUid(),item,BigDecimal.ZERO,order.getUserPhone(),order.getProvince(),order.getCity(),order.getDistrict(),order.getAddressDetail(),order.getGoodsDetail(),order.getGoodsType(),totalAmount,order.getHandlingMethod(),order.getPayType(),order.getLxChannelId(),appId);
        AlipayBean alipayBean = new AlipayBean();
        alipayBean.setOut_trade_no(out_trade_no);
        alipayBean.setSubject(order.getGoodsDetail());
        alipayBean.setTotal_amount(totalPrice);
//        alipayBean.setBody(body);
        return alipay.pay(alipayBean,appId);
    }

    @Override
    public String aliAppPay(Order order) throws AlipayApiException {
        String appId = order.getAppId();
        String out_trade_no = Utility.generateUUID();
        Long itemId = order.getItemId();
        Item item = itemService.findItemById(itemId);

        //订单总金额
        final String totalPrice = item.getUnitPrice().toString();
        BigDecimal totalAmount = new BigDecimal(totalPrice);
        //订单减免金额 随机抵扣1~3元 保留一位小数
        BigDecimal deductionAmount =new BigDecimal((double) (Math.random()*3) + 1);
        Double d=deductionAmount.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
        if(d>3) d=3.0;
        deductionAmount =new BigDecimal(String.valueOf(d));


        orderService.createOrder(out_trade_no,order.getCardSn(),order.getRealName(),order.getUid(),item,deductionAmount,order.getUserPhone(),order.getProvince(),order.getCity(),order.getDistrict(),order.getAddressDetail(),order.getGoodsDetail(),order.getGoodsType(),totalAmount,order.getHandlingMethod(),order.getPayType(),order.getLxChannelId(),appId);
        // 封装bizmodel信息
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(order.getGoodsDetail());
        model.setBody(JSON.toJSONString(order));
        model.setProductCode("QUICK_MSECURITY_PAY");
        model.setTotalAmount(String.valueOf(totalAmount.subtract(deductionAmount)));
        model.setTimeoutExpress("30m");
        return alipay.aliAppPay(model,appId);
    }

    @Override
    public String transactionDoH5(Order order,String redirectUrl, HttpServletRequest request) {
        String mWebUrl = "";
        String appId = order.getAppId();
        WechatProperties.Wechat wechat = this.wechatProperties.getMappings().get(appId);
        //获取请求ip地址
        String ip = getIpAddr(request);
        String out_trade_no = Utility.generateUUID();
        JSONObject object = new JSONObject();
        JSONObject h5Info = new JSONObject();
        object.put("type","Android");
        object.put("app_name","<XXXXX中文名字>");
        object.put("package_name","<XXXXX包名com.example.foo>");
        h5Info.put("h5_info",object);
        String scene_info = h5Info.toJSONString();
        try {
            WXPay wxpay = new WXPay(new IWxPayConfig(wechat));
            /*
             * sp 1. 拼装请求参数
             */
            Map<String, String> data = new HashMap<>();
            data.put("body", "美天优汇下单");
            data.put("out_trade_no", out_trade_no);
            data.put("total_fee", "1");
            data.put("spbill_create_ip", ip);
            data.put("notify_url", wechat.getTrade().getNotifyUrl()); // 订单回调接口
            data.put("trade_type", "MWEB");
            data.put("scene_info",scene_info);
            Map<String, String> response = wxpay.unifiedOrder(data);
            logger.info("微信h5支付下单, 返回值 response={}", response);
            String returnCode = response.get("return_code");
            if (!SUCCESS.equals(returnCode)) {
                return null;
            }
            String resultCode = response.get("result_code");
            if (!SUCCESS.equals(resultCode)) {
                return null;
            }
            mWebUrl = response.get("mweb_url");
            //支付完返回浏览器跳转的地址，如跳到查看订单页面
            String redirect_urlEncode =  URLEncoder.encode(redirectUrl,"utf-8");//对上面地址urlencode
            mWebUrl = mWebUrl + "&redirect_url=" + redirect_urlEncode;//拼接返回地址
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mWebUrl;
    }

    @Override
    public String payBack(String notifyData) {

        logger.info("payBack() start, notifyData={}", notifyData);
        String xmlBack="";
        Map<String, String> notifyMap = null;
        try {

            notifyMap = WXPayUtil.xmlToMap(notifyData);         // 转换成map
            String appId = notifyMap.get("appid");//微信分配的小程序ID

            WechatProperties.Wechat wechat = this.wechatProperties.getMappings().get(appId);

            WXPay wxpay = new WXPay(new IWxPayConfig(wechat));

            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                // 签名正确
                // 进行处理。
                // 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户侧订单状态从退款改成支付成功
                notifyMap.put("payment","wxpay");
                this.check(notifyMap);

                // 业务逻辑处理 ****************************
                logger.info("微信支付回调成功订单号: {}", notifyMap);
                //修改支付状态为已支付 订单状态为代发货
                process(notifyMap);

                xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[SUCCESS]]></return_msg>" + "</xml> ";
                return xmlBack;
            } else {
                logger.error("微信支付回调通知签名错误");
                xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                return xmlBack;
            }
        } catch (Exception e) {
            logger.error("微信支付回调通知失败",e);
            xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        return xmlBack;
    }

    @Override
    public String aliPayBack(HttpServletRequest request) {

        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        logger.info("支付宝回调，{}", paramsJson);
        try {
            String appId = params.get("app_id");
            AliPayProperties.Alipay alipay = this.aliPayProperties.getMappings().get(appId);
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipay.getPay().getPublicKey(), alipay.getOther().getCharset(), alipay.getOther().getSignType());
            if (signVerified) {
                logger.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                params.put("payment","alipay");
                this.check(params);
                AlipayNotifyParam param = buildAlipayNotifyParam(params);
                String trade_status = param.getTradeStatus();
                // 支付成功
                if (trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED")){
                    // 处理支付成功逻辑
                    process(params);
                }else{
                    logger.error("没有处理支付宝回调业务，支付宝交易状态：{},params:{}",trade_status,paramsJson);
                }
                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                return "success";
            } else {
                logger.info("支付宝回调签名认证失败，signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            logger.error("支付宝回调处理失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            return "failure";
        }
    }

    public void process(Map<String, String> params){

        String out_trade_no = params.get("out_trade_no");
        String appId = params.get("app_id");
        String payment = params.get("payment");
        String transaction_no = "";
        BigDecimal money = BigDecimal.ZERO;
        if(payment.equals("wxpay")){
            //微信单位为分
            money = new BigDecimal(params.get("total_fee")).divide(new BigDecimal("100"));
            transaction_no = params.get("transaction_id");
        }else if(payment.equals("alipay")){
            //支付宝单位为元
            money = new BigDecimal(params.get("total_amount"));
            transaction_no = params.get("trade_no");
        }
        Order order = orderService.findOrderByOutTradeNo(out_trade_no);

        //修改支付状态为已支付 订单状态为代发货
        if(order.getPayStatus().equals(Order.PayStatus.pending.val)){
            order.setPayPrice(money);
            order.setPayTime(LocalDateTime.now());
            order.setPayStatus(Order.PayStatus.processed.val);
            order.setTransactionNo(transaction_no);
            order.setStatus(Order.Status.pending.val);
            int row = orderService.updateOrder(order);

            if(row > 0){
                //ftt profit
                fttProfitFacadeService.updateFttProfits4Order(order.getUid(), order.getItemId(), order.getId());
            }

        }else{
            throw new OrderException(String.format("Order#%s 状态错误: %s", order.getId(),order.getPayStatus()));
        }
    }

    // 将request中的参数转换成Map
    private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }

    private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AlipayNotifyParam.class);
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     *
     * @param params
     * @throws AlipayApiException
     */
    private void check(Map<String, String> params) throws AlipayApiException {

        String out_trade_no = params.get("out_trade_no");
        String payment = params.get("payment");
        BigDecimal money = BigDecimal.ZERO;
        if(payment.equals("wxpay")){
            //微信单位为分
            money = new BigDecimal(params.get("total_fee")).divide(new BigDecimal("100"));
        }else if(payment.equals("alipay")){
            //支付宝单位为元
            money = new BigDecimal(params.get("total_amount"));
        }

        if (StringUtils.isEmpty(out_trade_no)) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
        Order order = orderService.findOrderByOutTradeNo(out_trade_no);
        if (order == null) {
            throw new AlipayApiException("out_trade_no错误");
        }

        // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
        if((order.getTotalPrice().subtract(order.getDeductionAmount())).compareTo(money) != 0){
            logger.error("微信支付回调通知金额不一致: {}", money);
            throw new AlipayApiException("微信支付回调通知金额不一致");
        }

    }

    /**
     * 获取用户实际ip
     * @param request
     * @return
     */
    public String getIpAddr(HttpServletRequest request){
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }



}
